#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>

#include "udev_include.h"

static struct udev *g_controller_udev = NULL;
static struct udev_enumerate *g_controller_iter = NULL;

static int udev_scan_ctl(const char *sysname, const char *scan_path, const char *scan_cmd)
{
    int fd = -1;
    int ret = -1;
    ssize_t bytes_written = 0;
    if (sysname == NULL || scan_path == NULL || scan_cmd == NULL) {
        return -1;
    }

    fd = open(scan_path, O_WRONLY, 0644);
    if (fd == -1) {
        perror("open");
        return 1;
    }

    printf("Scan %s", sysname);
    bytes_written = write(fd, scan_path, strlen(scan_cmd));
    if (bytes_written != strlen(scan_cmd)) {
        printf(" error.\n");
        ret = 1;
    } else {
        printf(" success.\n");
        ret = 0;
    }
    close(fd);
    return 0;
}

static void udev_scan_release(void)
{
    if (g_controller_iter != NULL) {
        udev_enumerate_unref(g_controller_iter);
    }
    if (g_controller_udev != NULL) {
        udev_unref(g_controller_udev);
    }
}

static int udev_scan_ctl_init(void)
{
    int ret = -1;
    g_controller_udev = udev_new();
    if (!g_controller_udev) {
        printf("Failed to create udev\n");
        return -1;
    }
    g_controller_iter = udev_enumerate_new(g_controller_udev);
    if (g_controller_iter == NULL) {
        udev_unref(g_controller_udev);
        printf("Failed to create udev_iter\n");
        return -1;
    }

    ret = udev_enumerate_add_match_subsystem(g_controller_iter, "iscsi_host");
    if (ret != 0) {
        printf("Failed to add match subsystem: iscsi_host\n");
        udev_scan_release();
        return -1;
    }

    ret = udev_enumerate_add_match_subsystem(g_controller_iter, "fc_host");
    if (ret != 0) {
        printf("Failed to add match subsystem: fc_host\n");
        udev_scan_release();
        return -1;
    }

    ret = udev_enumerate_add_match_subsystem(g_controller_iter, "nvme");
    if (ret != 0) {
        printf("Failed to add match subsystem: nvme\n");
        udev_scan_release();
        return -1;
    }
    return 0;
}

static int udev_scan_start(void)
{
    // int ret = 0;
    int proto = -1;
    char buf_path[100] = {0};
    const char *nvme_scan_cmd = "1";
    const char *scsi_scan_cmd = "- - -";
    const char *syspath = NULL;
    const char *sysname = NULL;
    device_t *device = NULL;
    struct udev_list_entry *entry = NULL;

    udev_enumerate_scan_devices(g_controller_iter);
    printf("Start scanning device\n");
    udev_list_entry_foreach(entry, udev_enumerate_get_list_entry(g_controller_iter)) {
        syspath = udev_list_entry_get_name(entry);
        if (syspath == NULL) {
            continue;
        }

        device = udev_device_new_from_syspath(g_controller_udev, syspath);
        if (device == NULL) {
            continue;
        }

        sysname = udev_device_get_sysname(device);
        if (sysname == NULL) {
            udev_device_unref(device);
            continue;
        }

        if (strncmp(sysname, "nvme", 4) == 0) {
            sprintf(buf_path, "/sys/class/nvme/%s/rescan_controller", sysname);
            udev_scan_ctl(sysname, buf_path, nvme_scan_cmd);
        } else if (strncmp(sysname, "host", 4) == 0) {
            sprintf(buf_path, "/sys/class/scsi_host/%s/scan", sysname);
            udev_scan_ctl(sysname, buf_path, scsi_scan_cmd);
        }
        udev_device_unref(device);
    }
    return 0;
}

int udev_scan_controller(void)
{
    int ret = -1;
    ret = udev_scan_ctl_init();
    if (ret != 0) {
        printf("Failed to init udev\n");
        return -1;
    }
    ret = udev_scan_start();
    if (ret != 0) {
        printf("Failed to start udev\n");
        return -1;
    }
    udev_scan_release();
    return 0;
}
